﻿using System;
using SharpShare.Storage;

namespace SharpShare.Afp.Protocol {
    internal static class StorageMetadata {
        private const string FinderInfoMetadataName = (Handlers.AfpSetExtAttributeRequestHandler.CustomMetadataNamePrefix + "com.apple.FinderInfo");
        private const string AccessRightsMetadataName = "com.sharpshare.afp.accessrights";
        private const string BackupDateMetadataName = "com.sharpshare.afp.backupdate";
        private const string UnixPermissionsMetadataName = "com.sharpshare.afp.unixpermissions";
        private const string OwnerGuidMetadataName = "com.sharpshare.afp.owner";
        private const string GroupGuidMetadataName = "com.sharpshare.afp.group";
        private const string ResourceForkMetadataName = "com.sharpshare.afp.resource-fork";

        public static IStorageDataProvider OpenResourceFork(this IStorageItem item, StorageFileAccess access, StorageFileAccess share, StorageFileMode mode) {
            IStorageDataProvider provider = item
                .OpenMetadata(ResourceForkMetadataName, access, share, mode);
            return provider;
        }

        public static void SetFinderInfo(this IStorageItem item, byte[] info) {
            if (info != null && info.Length != 32) {
                info = null;
            }

            item.AddMetadata(FinderInfoMetadataName, info);
        }
        public static byte[] GetFinderInfo(this IStorageItem item) {
            byte[] data = item.GetMetadata(FinderInfoMetadataName);

            if (data != null && data.Length != 32) {
                data = null;
            }

            if (data == null) {
                data = new byte[32];
            }

            return data;
        }

        public static void SetBackupDate(this IStorageItem item, DateTime date) {
            byte[] data = null;

            if (date != DateTime.MinValue) {
                data = BitConverter.GetBytes(date.ToUniversalTime().ToBinary());
            }

            item.AddMetadata(BackupDateMetadataName, data);
        }
        public static DateTime GetBackupDate(this IStorageItem item) {
            byte[] data = item.GetMetadata(BackupDateMetadataName);
            if (data != null && data.Length == 8) {
                DateTime date = DateTime.FromBinary(BitConverter.ToInt64(data, 0));
                return date;
            }

            return DateTime.MinValue;
        }

        public static AfpUnixPrivilegePermissions? GetUnixPermissions(this IStorageItem item) {
            string value = item.GetMetadataText(UnixPermissionsMetadataName);

            if (value == null) {
                return null;
            }

            int intValue;
            if (int.TryParse(value, out intValue)) {
                return (AfpUnixPrivilegePermissions)intValue;
            }

            return null;
        }
        public static void SetUnixPermissions(this IStorageItem item, AfpUnixPrivilegePermissions? rights) {
            string value = null;

            if (rights.HasValue) {
                value = ((int)rights.Value).ToString();
            }

            item.AddMetadata(UnixPermissionsMetadataName, value);
        }

        public static Guid? GetOwner(this IStorageItem item) {
            string value = item.GetMetadataText(OwnerGuidMetadataName);
            if (value == null) {
                return null;
            }

            Guid id;
            if (Guid.TryParse(value, out id)) {
                return id;
            }

            return null;
        }
        public static void SetOwner(this IStorageItem item, Guid? id) {
            string value = null;

            if (id.HasValue) {
                value = id.Value.ToString();
            }

            item.AddMetadata(OwnerGuidMetadataName, value);
        }

        public static Guid? GetGroup(this IStorageItem item) {
            string value = item.GetMetadataText(GroupGuidMetadataName);
            if (value == null) {
                return null;
            }

            Guid id;
            if (Guid.TryParse(value, out id)) {
                return id;
            }

            return null;
        }
        public static void SetGroup(this IStorageItem item, Guid? id) {
            string value = null;

            if (id.HasValue) {
                value = id.Value.ToString();
            }

            item.AddMetadata(GroupGuidMetadataName, value);
        }

        public static AfpAccessRightsBitmap? GetAccessRights(this IStorageItem item) {
            string value = item.GetMetadataText(AccessRightsMetadataName);

            if (value == null) {
                return null;
            }

            int intValue;
            if (int.TryParse(value, out intValue)) {
                return (AfpAccessRightsBitmap)intValue;
            }

            return null;
        }
        public static AfpAccessRightsBitmap GetAccessRights(this IStorageItem item, IAfpUser user) {
            AfpAccessRightsBitmap accessRights = (item.GetAccessRights() ?? 0);
            
            // Compute UA rights
            if (accessRights.HasFlag(AfpAccessRightsBitmap.UserIsOwner)) {
                accessRights ^= AfpAccessRightsBitmap.UserIsOwner;
            }

            if (accessRights.HasFlag(AfpAccessRightsBitmap.OtherRead)) {
                accessRights |= AfpAccessRightsBitmap.UserRead;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.OtherExecute)) {
                accessRights |= AfpAccessRightsBitmap.UserExecute;
            }
            if (accessRights.HasFlag(AfpAccessRightsBitmap.OtherWrite)) {
                accessRights |= AfpAccessRightsBitmap.UserWrite;
            }

            Guid ownerId = (item.GetOwner() ?? Guid.Empty);
            Guid groupId = (item.GetGroup() ?? Guid.Empty);

            if (ownerId == Guid.Empty) {
                accessRights |= AfpAccessRightsBitmap.UserIsOwner;
                accessRights |= AfpAccessRightsBitmap.UserReadWriteExecute;
            }

            if (user != null && user.Id == ownerId) {
                if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerRead)) {
                    accessRights |= AfpAccessRightsBitmap.UserRead;
                }
                if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerExecute)) {
                    accessRights |= AfpAccessRightsBitmap.UserExecute;
                }
                if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerWrite)) {
                    accessRights |= AfpAccessRightsBitmap.UserWrite;
                }

                accessRights |= AfpAccessRightsBitmap.UserIsOwner;
            }

            if (user != null && user.IsInGroup(groupId)) {
                if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerRead)) {
                    accessRights |= AfpAccessRightsBitmap.UserRead;
                }
                if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerExecute)) {
                    accessRights |= AfpAccessRightsBitmap.UserExecute;
                }
                if (accessRights.HasFlag(AfpAccessRightsBitmap.OwnerWrite)) {
                    accessRights |= AfpAccessRightsBitmap.UserWrite;
                }
            }

            return accessRights;
        }

        public static void SetAccessRights(this IStorageItem item, AfpAccessRightsBitmap? rights) {
            string value = null;

            if (rights.HasValue) {
                value = ((int)rights.Value).ToString();
            }

            item.AddMetadata(AccessRightsMetadataName, value);
        }
    }
}
